package metamodel.transformer;

import metamodel.*;

import java.util.Iterator;
import java.util.Collection;
import java.util.HashSet;

/**
 * User: felix
 * Date: 09.06.2005
 */
public class TopDownTransformingGraphTraverser {

    private ClassNode classNode;
    private TopDownGraphTransformer visitor;
    private boolean suppressCycling;
    private HashSet visited;

    public TopDownTransformingGraphTraverser(ClassNode classNode, TopDownGraphTransformer visitor, boolean suppressCycling){
        this.classNode = classNode;
        this.visitor = visitor;
        this.suppressCycling = suppressCycling;
        if (suppressCycling){
            visited=new HashSet(10);
        }
    }

    public Object iterate (Object data, Object parent){
        return typeCase(classNode,data,parent);
    }

    private Object  typeCase(ClassNode classNode,Object data,Object parent ) {
        if (classNode.isPoly()){
              ClassNode concreteClassNode = classNode.getPoly().getClazzForConcreteTypeName(data.getClass().getName());
              return iterateRecord(concreteClassNode, data,parent);
        }
        return iterateRecord(classNode, data, parent);
    }

    private Object iterateRecord(ClassNode classNode, Object data, Object parent) {

        Object ret = visitor.visitRecord(classNode, data, parent);
        for (Iterator iterator = classNode.getProperties().iterator(); iterator.hasNext();) {
            Property p =  (Property) iterator.next();
            iterateProperty(p, data,parent);
        }
        return ret;
    }

    private Object iterateProperty(Property p, Object data, Object parent) {

        final Object propValue = p.getValue(data);
        if (propValue==null){
            return null; //leaf node
        }



        if (p instanceof FlatProperty){
            FlatProperty flatProp=(FlatProperty) p;
            visitor.visitFlatProperty(flatProp,propValue,parent);
            return null; //leaf node
        }

        if (suppressCycling){
             if (visited.contains(propValue)){
                 visitor.visitCyclic(p,data,parent);
                 return null;   // and yes it's a leaf
             }
             visited.add(propValue);
         }

        if (p instanceof ObjectReference){
            ObjectReference objRef = (ObjectReference) p;
            Object innerParent = visitor.visitObjectReference(objRef, propValue, parent);
            return typeCase(objRef.getElementType(),propValue,innerParent);
        }

        if (p instanceof CollectionRef){
            CollectionRef collRef=(CollectionRef) p;
            Collection c=(Collection) propValue;
            Collection ret=visitor.visitCollection((CollectionRef)p,c,parent);//iterateObjectCollection(c, p),parent);
            return iterateObjectCollection(collRef, c, ret);
        }
        throw new RuntimeException("Unknown meta type");
    }



    private Object iterateObjectCollection(CollectionRef collRef, Collection data, Collection parent) {
        int index=0;
        for (Iterator iterator = data.iterator(); iterator.hasNext();) {
            Object collectionElement =  iterator.next();
            Object ret=visitor.visitCollectionElement(collRef,index,collectionElement,parent);
            typeCase(collRef.getElementType(),collectionElement,ret);
            index++;
        }
        return parent;// see the irregularity here!
    }
}
